package es.efor.ics.gev.dao.jpa;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.Map.Entry;

import javax.persistence.Query;

import org.apache.commons.collections.CollectionUtils;
import org.springframework.stereotype.Repository;

import es.efor.ics.gev.dao.OrganizacionDao;
import es.efor.ics.gev.model.AtributoInformado;
import es.efor.ics.gev.model.AtributoMaestro;
import es.efor.ics.gev.model.AtributoPerfil;
import es.efor.ics.gev.model.GenericAtributoInformado;
import es.efor.ics.gev.model.Organizacion;
import es.efor.ics.gev.model.Perfil;
import es.efor.ics.gev.model.PerfilOrganizacion;
import es.efor.ics.gev.model.PerfilOrganizativo;

/**
 * This class interacts with EntityManager to save/delete and retrieve
 * Organizacion objects.
 * 
 * @author dlisbona
 * 
 */
@Repository("organizacionDao")
public class OrganizacionDaoJpa extends GenericDaoJpa<Organizacion, Long>
		implements OrganizacionDao {

	/**
	 * Constructor that sets the entity to Organizacion.class.
	 */
	public OrganizacionDaoJpa() {
		super(Organizacion.class);
	}

	/**
	 * {@inheritDoc}
	 */
	@SuppressWarnings("unchecked")
	public List<Organizacion> getOrganizaciones() {
		return getEntityManager().createNamedQuery("org.findAllOrganizaciones")
				.getResultList();
	}

	/**
	 * {@inheritDoc}
	 */
	@SuppressWarnings("unchecked")
	public List<Organizacion> getOrganizacionesByCIF(String cif) {
		String value = '%' + cif.toUpperCase() + '%';
		return getEntityManager().createNamedQuery("org.findOrganizacionesByCIF")
				.setParameter("cif", value).getResultList();
	}

	/**
	 * {@inheritDoc}
	 */
	@SuppressWarnings("unchecked")
	public List<Organizacion> getOrganizacionesByCIFAndRazonSocial(String cif,
			String razonSocial) {
		String value1 = '%' + cif.toUpperCase() + '%';
		String value2 = '%' + razonSocial.toUpperCase() + '%';
		return getEntityManager().createNamedQuery(
				"org.findOrganizacionesByCIFAndRazonSocial").setParameter("cif",
				value1).setParameter("razonSocial", value2).getResultList();
	}

	/**
	 * {@inheritDoc}
	 */
	@SuppressWarnings("unchecked")
	public List<Organizacion> getOrganizacionesByCIFAndRazonSocialAndPerfil(
			String cif, String razonSocial, Long perfilId) {
		String value1 = '%' + cif.toUpperCase() + '%';
		String value2 = '%' + razonSocial.toUpperCase() + '%';
		return getEntityManager().createNamedQuery(
				"org.findOrganizacionesByCIFAndRazonSocialAndPerfilId")
				.setParameter("cif", value1)
				.setParameter("razonSocial", value2).setParameter("perfilId",
						perfilId).getResultList();
	}

	/**
	 * {@inheritDoc}
	 */
	@SuppressWarnings("unchecked")
	public List<Organizacion> getOrganizacionesByCIFAndRazonSocialAndPerfil(
			String cif, String razonSocial, String nombrePerfil) {
		String value1 = '%' + cif.toUpperCase() + '%';
		String value2 = '%' + razonSocial.toUpperCase() + '%';
		String value3 = '%' + nombrePerfil.toUpperCase() + '%';
		return getEntityManager().createNamedQuery(
				"org.findOrganizacionesByCIFAndRazonSocialAndNombrePerfil")
				.setParameter("cif", value1)
				.setParameter("razonSocial", value2).setParameter(
						"nombrePerfil", value3).getResultList();
	}

	/**
	 * {@inheritDoc}
	 */
	@SuppressWarnings("unchecked")
	public List<Organizacion> getOrganizacionesByPerfil(Long perfilId) {		
		return getEntityManager().createNamedQuery("org.findOrganizacionesByPerfilId")
		.setParameter("perfilId", perfilId).getResultList();	
	}

	/**
	 * {@inheritDoc}
	 */
	@SuppressWarnings("unchecked")
	public List<Organizacion> getOrganizacionesByPerfil(String nombrePerfil) {
		String value = '%' + nombrePerfil.toUpperCase() + '%';
		return getEntityManager().createNamedQuery("org.findOrganizacionesByNombrePerfil")
				.setParameter("nombrePerfil", value).getResultList();		
	}

	/**
	 * {@inheritDoc}
	 */
	@SuppressWarnings("unchecked")
	public List<Organizacion> getOrganizacionesByRazonSocial(String razonSocial) {
		String value = '%' + razonSocial.toUpperCase() + '%';
		return getEntityManager().createNamedQuery("org.findOrganizacionesByRazonSocial")
				.setParameter("razonSocial", value).getResultList();
	}

	/**
	 * {@inheritDoc}
	 */
	@SuppressWarnings("unchecked")
	public Organizacion getOrganizacionByCif(String cif) {
		Query q = getEntityManager().createNamedQuery("org.findOrganizacionByCIF");
        q.setParameter(1, cif);       
        List<Organizacion> organizaciones = q.getResultList();
        if (organizaciones != null && !organizaciones.isEmpty()) {            
            return organizaciones.get(0);
        }
		return null;
	}

	/**
	 * {@inheritDoc}
	 */
	public Organizacion createOrganizacion(String cif, String razonSocial,
			List<PerfilOrganizativo> perfiles) {
		Organizacion org = getEntityManager().merge(
				new Organizacion(cif, razonSocial));
		org.addPerfilesOrganizacion(perfiles);
		getEntityManager().flush();
		return org;
	}

	/**
	 * {@inheritDoc}
	 */
	public Organizacion createOrganizacion(String cif, String razonSocial,
			List<Organizacion> organizacionesHijo,
			List<PerfilOrganizativo> perfiles) {
		Organizacion org = getEntityManager().merge(
				new Organizacion(cif, razonSocial));
		org.addPerfilesOrganizacion(perfiles);
		org.addOrganizacionesHijo(organizacionesHijo);
		getEntityManager().flush();
		return org;
	}

	/**
	 * {@inheritDoc}
	 */
	public Organizacion saveOrganizacion(Organizacion org) {
		log.debug("organizacion's id: " + org.getId());
		Organizacion o = getEntityManager().merge(org);
		getEntityManager().flush();
		return o;
	}

	/**
	 * {@inheritDoc}
	 */	
	@SuppressWarnings("unchecked")
	public List<Perfil> getPerfilesOrganizacion(Long organizacionId) {		
		return getEntityManager().createNamedQuery("org.findPerfilesOrganizacion")
		.setParameter("id", organizacionId).getResultList();	
	}

	/**
	 * {@inheritDoc}
	 */
	public Organizacion addPerfil(Organizacion org, PerfilOrganizativo perfil) {
		if (org.isPerfilAsociado(perfil.getId())) {
			throw new IllegalArgumentException(
					"El perfil ya se encuentra asociado a la organizacion");
		}
		org.addPerfilOrganizacion(perfil, org.getNumPerfiles() + 1);
		getEntityManager().flush();
		return org;
	}

	/**
	 * {@inheritDoc}
	 */
	public Organizacion removePerfil(Organizacion org, PerfilOrganizativo perfil) {
		if (!org.isPerfilAsociado(perfil.getId())) {
			throw new IllegalArgumentException(
					"El perfil no se encuentra asociado a la organizacion");
		}
		PerfilOrganizacion perfilOrganizacion = org
				.getPerfilOrganizacion(perfil.getId());
		List<PerfilOrganizacion> perfilesToUpdate = org.getPerfilesOrderAbove(perfilOrganizacion);
		for (PerfilOrganizacion perfileToUpdate : perfilesToUpdate) {
			perfileToUpdate.setOrden(perfileToUpdate.getOrden() - 1);
		}
		org.removePerfilOrganizacion(perfil);
		getEntityManager().flush();
		return org;
	}

	/**
	 * {@inheritDoc}
	 */
	@SuppressWarnings("unchecked")
	public Organizacion updatePerfiles(Organizacion org,
			List<PerfilOrganizativo> perfiles) {

		Collection<PerfilOrganizativo> perfilesToDelete = CollectionUtils
				.subtract(org.getPerfiles(), perfiles);
		for (PerfilOrganizativo p : perfilesToDelete) {
			org.removePerfilOrganizacion(p);
		}

		for (int i = 0; i < perfiles.size(); i++) {
			int currentOrden = i + 1;
			PerfilOrganizativo p = perfiles.get(i);
			if (org.isPerfilAsociado(p.getId())) {
				org.getPerfilOrganizacion(p.getId()).setOrden(currentOrden);
			} else {
				org.addPerfilOrganizacion(p, currentOrden);
			}
		}
		getEntityManager().flush();
		return org;
	}

	/**
	 * {@inheritDoc}
	 */
	@SuppressWarnings("unchecked")
	public List<AtributoMaestro> getAtributosOrganizacion(Long organizacionId) {
		return getEntityManager().createNamedQuery("org.findAtributosOrganizacion")
				.setParameter("id", organizacionId).getResultList();
	}

	/**
	 * {@inheritDoc}
	 */
	public Integer getNextOrdenPerfiles(Long organizacionId) {
		return ((Long) getEntityManager().createNamedQuery(
				"org.findNextOrdenPerfilOrganizacion").setParameter("id",
				organizacionId).getSingleResult()).intValue();
	}

	/**
	 * {@inheritDoc}
	 */
	public Organizacion setValorAtributoOrganizacion(Organizacion org,
			AtributoMaestro atributo, String valor) {
		org.setValorAtributo(atributo, valor);
		getEntityManager().flush();
		return org;
	}

	/**
	 * {@inheritDoc}
	 */
	public Organizacion setValoresAtributosOrganizacion(Organizacion org,
			Map<AtributoMaestro, String> mapValores) {
		Set<Entry<AtributoMaestro, String>> entrySet = mapValores.entrySet();
		for (Iterator<Entry<AtributoMaestro, String>> it = entrySet.iterator(); it
				.hasNext();) {
			Map.Entry<AtributoMaestro, String> e = (Map.Entry<AtributoMaestro, String>) it
					.next();
			org.setValorAtributo(e.getKey(), e.getValue());
		}
		getEntityManager().flush();
		return org;
	}

	/**
	 * {@inheritDoc}
	 */
	public SortedMap<AtributoPerfil, String> getMapAtributosOrganizacion(
			Organizacion organizacion, Perfil perfil) {
		SortedMap<AtributoPerfil, String> result = new TreeMap<AtributoPerfil, String>();
		for (AtributoPerfil atributoPerfil : perfil.getAtributosPerfil()) {
			result.put(atributoPerfil, organizacion
					.getValorAtributoInformado(atributoPerfil.getId()
							.getAtributoId()));
		}
		return result;
	}

	/**
	 * {@inheritDoc}
	 */
	@SuppressWarnings("unchecked")
	public List<AtributoInformado> getValoresAtributosPerfil(Organizacion org,
			Perfil perfil) {
		List<AtributoInformado> result = new ArrayList<AtributoInformado>();
		for (AtributoPerfil a : perfil.getAtributosPerfil()) {
			result.add(new GenericAtributoInformado(a, org
					.getValorAtributoInformado(a.getId().getAtributoId())));
		}
		return result;
	}

}
